<?php
/**
 * Protoweb's base controller with base functionality
 */
include_once 'pwcontroller.php';

/**
 * The controller projects model
 *
 * Public controller methods are friendly urls thanks to {@link http://codeigniter.com
 * Codeigniter}.
 *
 * @author Gonzalez Federico, Viera Leonel
 * @package Protoweb
 * @subpackage controller
 */
class Projects extends PwController
{
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Security for projects action
     *
     * @param integer $project_id ID of the project the the logged user is trying to do something
     * @param string $level Can be logged, member or leader
     * @param string $url_error if an error ocurrs, the method redirects to this url
     * @return $User
     */
    public static function security($project_id, $level, $url_error="")
    {
        // Security from Users
        include_once 'users.php';
        $user = Users::security();

        if ($level == "logged") {
            return ($user);
        }
        else {
            $project = new Project($project_id);
          
            if ($user->is_member_of($project_id)) {
                return $user;
            }
            else {
                if ( ! strlen($url_error)) {
                    $url_error = base_url() . "users/dashboard/";
                }
                redirect($url_error);
            }
        } // else de if ($level == "logged") {
    } // end of method security($project_id,$level,$url_error="")

    /**
     * Active a previously deactivated project.
     *
     * When a project is active, everyone can work on it. Otherwise, when it is
     * deactivate, anyone can work on it.
     *
     * @param integer $id Project ID
     */
    public function activate($id)
    {
        // User security and relationship with project
        $user = Projects::security($id, "member");

        if ($id > 0) {
            $project = new Project($id);
            $project->active = TRUE;

            // Begin transaction
            $project->trans_begin();

            if ($project->save()) {
                // Commit )changes
                $project->trans_commit();
                redirect(base_url()."users/dashboard/");
            }
            else {
                // undo changes
                $project->trans_rollback();
                $data["error"] = $project->error->string;
            }
        } // if ($id > 0)
        else {
            die("Projecto nulo 0");
        }
    }// end of method active($id=0)


    /**
     * Deactive an active project.
     *
     * When a project is active, everyone can work on it. Otherwise, when it is
     * deactivate, anyone can work on it.
     *
     * @param integer $id Project ID
     */
    public function deactivate($id)
    {
        // User security and relationship with project
        $user = Projects::security($id, "member");

        if ($id > 0) {
            $project         = new Project($id);
            $project->active = FALSE;

            // Begin transaction
            $project->trans_begin();

            if ($project->save()) {
                // Commit changes
                $project->trans_commit();
                redirect(base_url()."users/dashboard/");
            }
            else {
                // undo changes
                $project->trans_rollback();
                $data["error"] = $project->error->string;
            }
        } // if ($id > 0)
        else {
            die("Projecto nulo 0");
        }
    }// end of method deactive($id=0)

    /**
     * Deletes a project of Protoweb's database, deletes its DB and its files
     *
     * @param integer $id ID of the project to delete
     */
    public function delete($id)
    {
        // User security and relationship with project
        $user    = Projects::security($id, "member");
        $project = new Project($id);

        $this->load->dbforge();
        
        if ($this->dbforge->drop_database('IF EXISTS ' . $project->slug)) {
            
            delete_files($project->base_path(),TRUE);
            
            if (is_dir($project->base_path())) {
                rmdir($project->base_path());
            }

            if ($project->delete()) {
                redirect(base_url() . "users/dashboard/");
            }
        }
    }// end method delete($id=0)

    /**
     *  Loads create project view
     */
    public function create()
    {
        // Checking logged user
        $data["user"]    = Projects::security(0, "logged");
        $data["project"] = new Project();

        if ($this->input->server('REQUEST_METHOD') === 'POST') {
            $data = $this->create_save();
        }

        $this->smartyview->render('project_create.html', $data);
    } // end of method create

    /**
     * Saves project creation
     *
     * This method copy basic Protoweb files for the generated project, as well
     * as sets up config options and creates generated priject's database.
     * Also, records project in Protoweb's database.
     *
     * @param User $user The instance of the user that is creating the project
     * @return If error return $data to re-parse the view, if not error redirects to project dashboard
     */
    private function create_save()
    {
        $data['user'] = Projects::security(0, "logged");

        $data['project']              = new Project();
        $data['project']->slug        = $this->input->post('slug');
        $data['project']->name        = $this->input->post('name');
        $data['project']->description = $this->input->post('description');
        $data['project']->active      = TRUE;
        $on_error_del_files           = FALSE;
        $on_error_del_db              = FALSE;

        try {

            if ( ! $data["project"]->save($data['user'])) {
                throw new Exception($data["project"]->error->string);
            }

            // Increase script execution time, in order to let the unzip complete
            set_time_limit(120);

            $this->load->library('zip');

            // If exists deletes the directory of the especified path
            delete_files($data["project"]->base_path(),TRUE);
            if (is_dir($data["project"]->base_path())) {
                rmdir($data["project"]->base_path());
            }

            //unzip Protoweb base files
            if ( ! $this->zip->unzip($this->config->item('base_app_zip'),$data["project"]->base_path())) {
                throw new Exception("<p>Can't unzip files to: " . $data["project"]->base_path() . "</p>");
            }

            $on_error_del_files = TRUE;

            // Setting up config and database files
            $data["db"]              = $this->db;
            $data["base_url"]        = $data["project"]->base_url();
            $data['htacces_rewrite'] = preg_replace('/(.*)\/protoweb/', '/protoweb', base_url()) . 
                                       'projects/' . $data['project']->slug . '/';

            $this->smartyview->smarty->assign($data);
            $database_file      = $this->smartyview->smarty->fetch($this->config->item("generators_path")."database_template.html");
            $config_file        = $this->smartyview->smarty->fetch($this->config->item("generators_path")."config_template.html");
            $htacces            = $this->smartyview->smarty->fetch($this->config->item("generators_path")."htaccess_template.html");
            $project_model      = $this->smartyview->smarty->fetch($this->config->item('project_model_template'));
            $project_controller = $this->smartyview->smarty->fetch($this->config->item('project_controller_template'));

            // Writing project's database, htaccess and config files
            if ( ! write_file($data["project"]->config_path() . "database.php", $database_file, "w")) {
                throw new Exception("<p>Can't write file: " . $data["project"]->config_path() . "database.php</p>");
            }
            else {
                chmod($data["project"]->config_path() . "database.php", 0777);
            }

            if ( ! write_file($data["project"]->config_path() . "config.php", $config_file, "w")) {
                throw new Exception("<p>Can't write file: " . $data["project"]->config_path() . "config.php</p>");
            }
            else {
                chmod($data["project"]->config_path() . "config.php", 0777);
            }

            if ( ! write_file($data["project"]->base_path() . ".htaccess", $htacces, "w")) {
                throw new Exception("<p>Can't write file: " . $data["project"]->base_path() . ".htaccess</p>");
            }
            else {
                chmod($data["project"]->base_path() . ".htaccess", 0777);
            }

            file_put_contents($data["project"]->modelFilePath(), $project_model);
            chmod($data["project"]->modelFilePath(), 0777);

            file_put_contents($data["project"]->controllerFilePath(), $project_controller);
            chmod($data["project"]->controllerFilePath(), 0777);

            // Database creation
            $this->load->dbforge();
            $this->dbforge->drop_database("IF EXISTS " . $data["project"]->slug);
            if ( ! $this->dbforge->create_database($data["project"]->slug . 
                                   " DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci")) {
                throw new Exception("<p>Can't create database: " . $data["project"]->slug . "</p>");
            }

            $on_error_del_db = TRUE;

            // Overwrite header and footer files
            $this->overwrite_headers($data["project"]);        
              
            redirect(base_url() . "projects/dashboard/" . $data["project"]->id) .'/';
        } // try
        catch (Exception $exc) {
            // Delete's from Protoweb's projects database
            $data["project"]->delete();

            if ($on_error_del_files) {
                // Remove created files
                delete_files($data["project"]->base_path(),TRUE);
                if (is_dir($data["project"]->base_path())) {
                    rmdir($data["project"]->base_path());
                }
            }

            if ($on_error_del_db) {
                // Dropping database if exists
                $this->load->dbutil();
                if (in_array($data["project"]->slug, $this->dbutil->list_databases())) {
                    $this->load->dbforge();
                    $this->dbforge->drop_database("IF EXISTS " . $data["project"]->slug);
                }
            }
            
            $data['error'] = $exc->getMessage();

            return ($data);
        } // catch
    } // end of method save_create()

    /**
     * Overwrite header and footer with project name and description
     */
    private function overwrite_headers($project) 
    {
        // Overwriting header.html and footer.html with Project Name and Description
        $header = file_get_contents($project->views_path() . "header.html");
        $header = preg_replace(
                    "/(<pw:project-name>(.*)<\/pw:project-name>)/",
                    "<pw:project-name>".$project->name."</pw:project-name>",
                    $header
                  );
        write_file($project->views_path() . "header.html", $header, "w");
        
        $footer = file_get_contents($project->views_path() . "footer.html");
        $footer = preg_replace(
                    "/(<pw:project-name>(.*)<\/pw:project-name>)/",
                    "<pw:project-name>".$project->name."</pw:project-name>",
                    $footer
                  );
        $footer = preg_replace(
                    "/(<pw:project-description>(.*)<\/pw:project-description>)/",
                    "<pw:project-description>".$project->description."</pw:project-description>",
                    $footer
                  );
        write_file($project->views_path() . "footer.html", $footer, "w");
    }

    /**
     * Loads project dashboard view
     *
     * @param integer $id ID of the project
     */
    public function dashboard($id)
    {
        $data['user']    = Projects::security($id, 'member');
        $data['project'] = new Project($id);

        // Checking if the class_diagram PNG were generated
        if (file_exists($data['project']->docs_path().'class_diagram_minimal.png') AND
            file_exists($data['project']->docs_path().'class_diagram_full.png')) {
            
            $data['show_class_diagram_buttons'] = 'YES';

            // checking if width fits
            $img_info   = getimagesize($data['project']->docs_path().'class_diagram_minimal.png');
            $img_width  = $img_info[0];
            if ($img_width > 300) {
                $data['class_diagram_width'] = 300;
            }
            else {
                $data['class_diagram_width'] = $img_width;
            }
        }
        else {
            $data['show_class_diagram_buttons'] = 'NO';
        }

        $this->smartyview->render('project_dashboard.html', $data);
    } // end of method dashboard($id=0)

    /**
     * Loads project edit view
     *
     * @param integer $id Project Id
     */
    public function edit($id)
    {
      // User security and relationship with project
      $data['user']    = Projects::security($id, "member");
      $data['project'] = new Project($id);

      if ($this->input->server('REQUEST_METHOD') === 'POST') {
          $data = $this->edit_save();
      }
      
      $this->smartyview->render("project_edit.html", $data);
    }

    /**
     * Save posted changes of an edited project
     */
    private function edit_save() 
    {
        // User security and relationship with project
        $data['user'] = Projects::security($this->input->post("id"), "member");

        // Get new atribute values
        $data['project']              = new Project($this->input->post("id"));
        $data['project']->name        = $this->input->post("name");
        $data['project']->description = $this->input->post("description");

        // Setting up default entity
        $data["default_entity"] = $this->input->post("default_entity");
        $this->smartyview->smarty->assign($data);
        $routes_file = $this->smartyview->smarty->fetch($this->config->item("generators_path").
                                                        "routes_templates.html");

        // Writing routes file
        if (write_file($data['project']->config_path() . "routes.php", $routes_file, "w")) {
            chmod($data['project']->config_path() . "routes.php", 0777);
        }

        // Overwrite header and footer files
        $this->overwrite_headers($data['project']);
          
        // Begin transaction
        $data['project']->trans_begin();

        if ($data['project']->save()) {
            $data['project']->trans_commit();

            redirect(base_url()."projects/edit/".$data['project']->id."/");
        }
        else {
            //undo changes
            $data['project']->trans_rollback();

            $data["error"] = $data['project']->error->string;

            return $data;
        } // else de if ($project->save())
    } // end Method edit_save($id=0)

    /**
     * Loads project members view
     *
     * @param integer $id Project id
     */
    public function members($id)
    {
        // User security and relationship with project
        $data['user']    = Projects::security($id, "logged");
        $data['project'] = new Project($id);

        if ($this->input->server('REQUEST_METHOD') === 'POST') {
            $this->members_save($id);
        }
        
        $this->smartyview->render("project_members.html", $data);
    }


    /**
     * Save the relationship between Project and posted users
     *
     * @param integer $id Project ID
     */
    private function members_save($id)
    {
        // User security and relationship with project
        $data['user']    = Projects::security($id, "logged");
        $data['project'] = new Project($id);

        $data['project']->trans_begin();

        $usersAux = $this->input->post("developers");

        //Update relation users-proyects
        $users = $data['project']->update_multiple_relations_with("user",$usersAux);

        // SAVE ALL (form basics and model relations)
        $saved = $data['project']->save($users);

        if ($saved) {
            $data['project']->trans_commit();
            redirect(base_url()."projects/members/" . $id . "/");
        }
        else {
            $data['project']->trans_rollback();
            $data["error"] = $data['project']->error->string;

            $data['developers'] = new User();
            foreach ($this->input->post('developers') as $developer) {
                $data['developers']->or_where('id',$developer);
            }
            $data['developers']->get();

            $this->smartyview->render("project_members.html", $data);
        }// esle
    } // end Method members_save($id)

    /**
     * Generates class diagram for generated project
     *
     * @param integer $id ID of the project
     */
    public function make_class_diagram($id)
    {
        // disable warning and error outputs temporally
        error_reporting(0);

        $project               = new Project($id);
        $class_diagram_minimal = $project->docs_path() . "class_diagram_minimal.png";
        $class_diagram_thumb   = $project->docs_path() . "class_diagram_minimal_thumb.png";
        $class_diagram_full    = $project->docs_path() . "class_diagram_full.png";

        $yuml_full = "http://yuml.me/diagram/class/";
        foreach ($project->entity as $entity) {
            // listamos los atributos de cada entidad
            $yuml_full .= "[$entity->model_name|";
            foreach($entity->attribute as $attribute) {
                $yuml_full .= "$attribute->name;";
            }

            // listamos los actions de cada entidad
            $yuml_full .= "|";
            foreach($entity->action as $action) {
                $yuml_full .= "$action->name();";
            }
            $yuml_full .= "],";
        }

        foreach ($project->entity as $entity) {
            // listamos las relaciones
            foreach($entity->hasmany as $toMany) {
                $yuml_full .= "[$entity->model_name]>[$toMany->model_name],";
            }
        }

        copy($yuml_full, $class_diagram_full);
        chmod($class_diagram_full, 0777);
        
        $yuml_minimal = "http://yuml.me/diagram/dir:TB;/class/";
        foreach ($project->entity as $entity) {

            $yuml_minimal .= "[$entity->model_name],";

            foreach($entity->hasmany as $toMany) {
                $yuml_minimal .= "[$entity->model_name]>[$toMany->model_name],";
            }
        }
        copy($yuml_minimal, $class_diagram_minimal);
        chmod($class_diagram_minimal, 0777);

        copy($class_diagram_minimal, $class_diagram_thumb);
        chmod($class_diagram_thumb, 0777);

        redirect(base_url()."projects/dashboard/".$project->id."/");
    } // end of method make_class_diagram

} // end of class Projects

/* End of file projects.php */
/* Location: ./application/controllers/projects.php */